import { NodeSSH } from 'node-ssh';
import { fileURLToPath } from 'url';
import { dirname } from 'path';
import fs from 'fs';
import { execSync } from 'child_process';

const __filename = fileURLToPath(import.meta.url);
const __dirname = dirname(__filename);

const config = {
    host: '82.156.236.116',
    username: 'ubuntu',
    password: 'Y7g!k$2Q',
    remotePath: '/app/1panel/apps/openresty/openresty/www/sites/skill.test.com/index',
    // 连接配置
    readyTimeout: 20000, // 20秒连接超时
    keepaliveInterval: 5000, // 5秒心跳间隔
    keepaliveCountMax: 3 // 最大心跳失败次数
};

// 重试配置
const RETRY_CONFIG = {
    maxRetries: 3,
    retryDelay: 2000, // 2秒重试间隔
    backoffMultiplier: 2 // 指数退避
};

// 延迟函数
function delay(ms) {
    return new Promise(resolve => setTimeout(resolve, ms));
}

// 重试函数
async function retryOperation(operation, operationName, maxRetries = RETRY_CONFIG.maxRetries) {
    let lastError;
    
    for (let attempt = 1; attempt <= maxRetries; attempt++) {
        try {
            console.log(`${operationName} - 尝试 ${attempt}/${maxRetries}`);
            return await operation();
        } catch (error) {
            lastError = error;
            console.error(`${operationName} - 第 ${attempt} 次尝试失败:`, error.message);
            
            if (attempt < maxRetries) {
                const delayTime = RETRY_CONFIG.retryDelay * Math.pow(RETRY_CONFIG.backoffMultiplier, attempt - 1);
                console.log(`等待 ${delayTime}ms 后重试...`);
                await delay(delayTime);
            }
        }
    }
    
    throw new Error(`${operationName} 在 ${maxRetries} 次尝试后仍然失败: ${lastError.message}`);
}

// 连接验证函数
async function validateConnection(ssh) {
    try {
        console.log('验证服务器连接...');
        const result = await ssh.execCommand('echo "connection test"');
        if (result.stdout !== 'connection test') {
            throw new Error('连接验证失败');
        }
        console.log('连接验证成功');
        return true;
    } catch (error) {
        throw new Error(`连接验证失败: ${error.message}`);
    }
}

// 检查用户权限
async function checkUserPermissions(ssh) {
    try {
        console.log('检查用户权限...');
        
        // 检查当前用户
        const whoami = await ssh.execCommand('whoami');
        console.log(`当前用户: ${whoami.stdout}`);
        
        // 检查sudo权限
        const sudoCheck = await ssh.execCommand('sudo -n true 2>/dev/null && echo "has_sudo" || echo "no_sudo"');
        console.log(`Sudo权限: ${sudoCheck.stdout === 'has_sudo' ? '有' : '无'}`);
        
        // 检查用户组
        const groups = await ssh.execCommand('groups');
        console.log(`用户组: ${groups.stdout}`);
        
        return {
            user: whoami.stdout.trim(),
            hasSudo: sudoCheck.stdout === 'has_sudo',
            groups: groups.stdout.trim()
        };
    } catch (error) {
        console.warn('权限检查失败:', error.message);
        return null;
    }
}

// 检查磁盘空间
async function checkDiskSpace(ssh, remotePath) {
    try {
        console.log('检查服务器磁盘空间...');
        const result = await ssh.execCommand(`df -h ${remotePath} | tail -1 | awk '{print $5}' | sed 's/%//'`);
        const usage = parseInt(result.stdout);
        
        if (usage > 90) {
            console.warn(`警告: 磁盘使用率已达 ${usage}%，可能影响上传`);
        } else {
            console.log(`磁盘使用率: ${usage}%`);
        }
        
        return usage;
    } catch (error) {
        console.warn('无法检查磁盘空间:', error.message);
        return null;
    }
}

async function uploadFiles() {
    const ssh = new NodeSSH();

    try {
        // 使用重试机制连接服务器
        await retryOperation(async () => {
            console.log('正在连接服务器...');
            await ssh.connect(config);
            console.log('连接成功！');
            
            // 验证连接
            await validateConnection(ssh);
        }, '服务器连接');

        // 检查用户权限
        const userPerms = await checkUserPermissions(ssh);
        
        // 检查磁盘空间
        await checkDiskSpace(ssh, config.remotePath);

        // 检查并设置远程目录权限
        console.log('检查远程目录...');
        const result = await ssh.execCommand(`[ -d "${config.remotePath}" ] && echo "exists" || echo "not exists"`);
        if (result.stdout === 'not exists') {
            console.log('创建远程目录...');
            await ssh.execCommand(`sudo mkdir -p ${config.remotePath}`);
        }
        
        // 确保目录权限正确
        console.log('设置目录权限...');
        await ssh.execCommand(`sudo chown -R ubuntu:ubuntu ${config.remotePath}`);
        await ssh.execCommand(`sudo chmod -R 755 ${config.remotePath}`);
        
        // 验证权限设置
        const permCheck = await ssh.execCommand(`ls -la ${config.remotePath}`);
        console.log('目录权限信息:', permCheck.stdout);
        
        // 测试写入权限
        const writeTest = await ssh.execCommand(`touch ${config.remotePath}/test_write_permission && rm ${config.remotePath}/test_write_permission`);
        if (writeTest.code !== 0) {
            throw new Error('目录写入权限测试失败，请检查服务器权限设置');
        }
        console.log('写入权限验证通过');

        // 检查本地.output/public目录大小
        try {
            const { execSync } = require('child_process');
            const sizeOutput = execSync('du -sh .output/public', { encoding: 'utf8' });
            console.log(`本地文件大小: ${sizeOutput.split('\t')[0]}`);
        } catch (error) {
            console.warn('无法获取本地文件大小:', error.message);
        }

        // 使用重试机制上传文件
        await retryOperation(async () => {
            console.log('开始上传文件...');
            let uploadErrors = [];
            let successCount = 0;
            let totalFiles = 0;
            
            // 先清空目标目录以避免权限冲突
            console.log('清空目标目录...');
            await ssh.execCommand(`sudo rm -rf ${config.remotePath}/*`);
            await ssh.execCommand(`sudo chown -R ubuntu:ubuntu ${config.remotePath}`);
            
            await ssh.putDirectory('.output/public', config.remotePath, {
                recursive: true,
                concurrency: 3, // 进一步降低并发数
                tick: (localPath, remotePath, error) => {
                    totalFiles++;
                    if (error) {
                        console.error(`上传失败: ${localPath} -> ${remotePath}`, error.message);
                        uploadErrors.push({ localPath, remotePath, error: error.message });
                    } else {
                        successCount++;
                        if (successCount % 20 === 0) {
                            console.log(`已成功上传 ${successCount} 个文件...`);
                        }
                    }
                }
            });
            
            console.log(`上传统计: 成功 ${successCount}/${totalFiles} 个文件`);
            
            // 上传完成后修复权限
            if (successCount > 0) {
                console.log('修复上传文件权限...');
                await ssh.execCommand(`sudo chown -R ubuntu:ubuntu ${config.remotePath}`);
                await ssh.execCommand(`sudo chmod -R 644 ${config.remotePath}/*`);
                await ssh.execCommand(`sudo find ${config.remotePath} -type d -exec chmod 755 {} \\;`);
            }
            
            // 如果有上传错误，抛出异常以触发重试
            if (uploadErrors.length > 0) {
                console.error('上传失败的文件列表:');
                uploadErrors.slice(0, 10).forEach(({ localPath, error }) => {
                    console.error(`  - ${localPath}: ${error}`);
                });
                if (uploadErrors.length > 10) {
                    console.error(`  ... 还有 ${uploadErrors.length - 10} 个文件失败`);
                }
                throw new Error(`有 ${uploadErrors.length} 个文件上传失败`);
            }
        }, '文件上传');

        console.log('所有文件上传完成！');
        
        // 验证上传结果
        try {
            const verifyResult = await ssh.execCommand(`find ${config.remotePath} -type f | wc -l`);
            console.log(`远程文件数量: ${verifyResult.stdout.trim()}`);
        } catch (error) {
            console.warn('无法验证上传结果:', error.message);
        }
        
    } catch (error) {
        console.error('上传过程中发生错误:', error.message);
        console.error('详细错误信息:', error);
        process.exit(1);
    } finally {
        ssh.dispose();
    }
}

// 检查构建是否成功
if (!fs.existsSync('.output/public')) {
    console.log('正在构建项目...');
    try {
        console.log('执行构建命令: npm run build');
        execSync('npm run build', { stdio: 'inherit' });
        console.log('构建完成！');
    } catch (error) {
        console.error('构建失败:', error.message);
        process.exit(1);
    }
} else {
    console.log('发现已存在的 .output/public 目录');
}

// 检查.output/public目录是否为空
try {
    const files = fs.readdirSync('.output/public');
    if (files.length === 0) {
        console.error('错误: .output/public 目录为空，请检查构建过程');
        process.exit(1);
    }
    console.log(`.output/public 目录包含 ${files.length} 个文件/文件夹`);
} catch (error) {
    console.error('无法读取 .output/public 目录:', error.message);
    process.exit(1);
}

console.log('开始上传流程...');
uploadFiles();
